LieLieBai = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceConsole-2.0")
local L = AceLibrary("AceLocale-2.2"):new("LieLieBai")

local __G = __G or _G or getfenv(0)
if not __G._G then
  __G._G = __G
end

-- 依赖项不做过多检查 --
local CleveRoids = _G.CleveRoids

function LieLieBai:OnInitialize()
  self.spellCache = {}
end

function LieLieBai:OnEnable()
  self:Print("LieLieBai:OnEnable!")
  self:RegisterEvent("PLAYER_ENTERING_WORLD", "OnWorldEnter")
  self:RegisterEvent("PLAYER_LEAVING_WORLD", "OnWorldLeave")
end

function LieLieBai:OnDisable()

end

function LieLieBai:OnWorldEnter()
  self:Print("LieLieBai:OnWorldEnter!")
end

function LieLieBai:OnWorldLeave()
  self:Print("LieLieBai:OnWorldLeave!")
end

function LieLieBai:GetLastPetModeGlobalID()
  local theLastPetModeGlobalID = nil
  if PetHasActionBar() then
    for i = 1, NUM_PET_ACTION_SLOTS, 1 do
      local slotspellname, slotspellsubtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled =
        GetPetActionInfo(i)
      if isToken and slotspellname == "PET_MODE_AGGRESSIVE" and isActive then
        theLastPetModeGlobalID = "PET_MODE_AGGRESSIVE"
        break
      elseif isToken and slotspellname == "PET_MODE_DEFENSIVE" and isActive then
        theLastPetModeGlobalID = "PET_MODE_DEFENSIVE"
        break
      elseif isToken and slotspellname == "PET_MODE_PASSIVE" and isActive then
        theLastPetModeGlobalID = "PET_MODE_PASSIVE"
        break
      end
    end
  end
  return theLastPetModeGlobalID
end

function LieLieBai:restorePetMode(theLastPetModeGlobalID)
  if theLastPetModeGlobalID == "PET_MODE_AGGRESSIVE" then
    Printd("restorePetMode: PET_MODE_AGGRESSIVE")
    PetAggressiveMode()
  elseif theLastPetModeGlobalID == "PET_MODE_DEFENSIVE" then
    Printd("restorePetMode: PET_MODE_DEFENSIVE")
    PetDefensiveMode()
  elseif theLastPetModeGlobalID == "PET_MODE_PASSIVE" then
    Printd("restorePetMode: PET_MODE_PASSIVE")
    PetPassiveMode()
  end
end

function LieLieBai:JiaSi()
  local spellId = self:getSpellId(L["假死"])
  if not spellId then
    return
  end

  if not UnitAffectingCombat("player") then
    return
  end

  local hasTarget = UnitExists('target')

  if hasTarget then
    ClearTarget()
  end

  CastSpell(spellId, BOOKTYPE_SPELL)

  if hasTarget then
    MyTimer:After(1.5, function()
      TargetLastTarget()
    end)
  end
end

function LieLieBai:JiaSiBingDong()
  local spellId = self:getSpellId("冰冻陷阱")
  if not spellId then
    return
  end

  if UnitAffectingCombat("player") then
    self:JiaSi()
  else
    CastSpell(spellId, BOOKTYPE_SPELL)
  end
end

function LieLieBai:JiaSiBingShuang()
  local spellId = self:getSpellId("冰霜陷阱")
  if not spellId then
    return
  end

  if UnitAffectingCombat("player") then
    self:JiaSi()
  else
    CastSpell(spellId, BOOKTYPE_SPELL)
  end
end

function LieLieBai:ShanDianTuXi()
  if UnitExists('target') then
    CastSpellByName(L["闪电吐息"])
  end
end

function LieLieBai:ShouHu()
  local spellName = IsShiftKeyDown() and L["雄鹰守护"] or L["猎豹守护"]
  if not buffed(spellName) then
    CastSpellByName(spellName)
  end
end

function LieLieBai:getSpellId(spellName)
  if not self.spellCache then
    self.spellCache = {}
  end

  if self.spellCache[spellName] then
    local cachedId = self.spellCache[spellName]
    local name = GetSpellName(cachedId, "BOOKTYPE_SPELL")
    if name == spellName then
      return cachedId
    else
      self.spellCache[spellName] = nil
    end
  end

  local _, _, offset, numSpells = GetSpellTabInfo(GetNumSpellTabs())
  local numAllSpell = offset + numSpells

  for i = 1, numAllSpell do
    local name = GetSpellName(numAllSpell - i + 1, "BOOKTYPE_SPELL")
    if (name == spellName) then
      local spellId = numAllSpell - i + 1
      self.spellCache[spellName] = spellId
      return spellId
    end
  end

  return nil
end

function LieLieBai:isInGCD()
  local spellId = self:getSpellId(L["毒蛇钉刺"])
  if not spellId then
    return false
  end
  local _, gcd = GetSpellCooldown(spellId, "BOOKTYPE_SPELL")
  return gcd == 1.5
end

function LieLieBai:IsSpellReady(spellID)
  local start, duration = GetSpellCooldown(spellID, BOOKTYPE_SPELL)
  return start == 0 or duration < 0.01
end

function LieLieBai:CastSpellIfReady(spellName)
  local spellId = self:getSpellId(spellName)

  if not spellId then
    self:Print("无法找到技能：" .. spellName)
    return false
  end

  if self:IsSpellReady(spellId) then
    CastSpell(spellId, BOOKTYPE_SPELL)
    return true
  else
    return false
  end
end

function LieLieBai:DuXie()
  if not self:isInGCD() and not buffed(L["毒蝎钉刺"], "target") then
    cast(L["毒蝎钉刺"])
    return true
  end
  return false
end

function LieLieBai:DuShe()
  if not self:isInGCD() and not buffed(L["毒蛇钉刺"], "target") then
    cast(L["毒蛇钉刺"])
    return true
  end
  return false
end

function LieLieBai:KuiShe()
  if not self:isInGCD() and not buffed(L["蝰蛇钉刺"], "target") then
    cast(L["蝰蛇钉刺"])
    return true
  end
  return false
end

function LieLieBai:AutoShot()
  local spellId = self:getSpellId(L["自动射击"])
  if spellId and not IsAutoRepeatAction(49) then
    CastSpell(spellId, BOOKTYPE_SPELL)
    return true
  end

  return false
end

function LieLieBai:TargetAttack()
  local isControlDown = IsControlKeyDown()
  local isShiftDown = IsShiftKeyDown()
  local hasMouseover = UnitExists("mouseover")

  if not isControlDown then
    CleveRoids.DoCast("[nodebuff:" .. L["猎人印记"] .. ",harm,nodead] " .. L["猎人印记"])
  end

  if isShiftDown then
    return
  end

  local petAttackTarget = (isControlDown and hasMouseover) and "[@mouseover,harm,nodead]" or "[harm,nodead]"
  CleveRoids.DoPetAction(PetAttack, petAttackTarget)

  local spellTarget = isControlDown and hasMouseover and "[@mouseover,harm,nodead] " or "[harm,nodead] "

  if self:PetCanUseSpell("俯冲") then
    CleveRoids.DoCast(spellTarget .. "俯冲")
  elseif self:PetCanUseSpell("突进") then
    CleveRoids.DoCast(spellTarget .. "突进")
  end
end

function LieLieBai:YouErSheji()
  local spellID = self:getSpellId(L["诱饵射击"])
  if IsUsableAction(52) and LieLieBai:IsSpellReady(spellID) then
    CastSpell(spellID, BOOKTYPE_SPELL)
  end
end

function LieLieBai:CastStingSpell(stingSpellName)
  local spellId = self:getSpellId(stingSpellName)
  if spellId and not self:isInGCD() and not buffed(stingSpellName, "target") then
    CastSpell(spellId, BOOKTYPE_SPELL)
    return true
  end
  
  return false
end

function LieLieBai:IsTargetImmune()
  local IMMUNE_TYPES = {
    ["元素生物"] = true,
    ["机械"] = true,
    ["构造体"] = true
  }
  
  local classification = UnitClassification("target")
  if classification == "worldboss" or classification == "elite" or classification == "rareelite" then
    return true
  end
  
  local creatureType = UnitCreatureType('target')
  return creatureType and IMMUNE_TYPES[creatureType] or false
end

function LieLieBai:SmartSting(stingSpellName)
  if not UnitExists('target') then
    return false
  end

  if UnitIsDeadOrGhost('target') then
    return false
  end

  if stingSpellName == "none" then
    return false
  elseif stingSpellName ~= nil then
    return self:CastStingSpell(stingSpellName)
  end

  if self:IsTargetImmune() then
    return false
  end

  local powerType = UnitPowerType("target")
  local mana = UnitMana("target") or 0
  if powerType ~= 0 or mana == 0 then
    return self:DuXie()
  else
    return self:DuShe()
  end
end

function LieLieBai:PetHasSpell(spellName)
  if not (UnitHealth("pet") > 0) then
    return false
  end

  for i = 1, NUM_PET_ACTION_SLOTS, 1 do
    local slotspellname, slotspellsubtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled =
      GetPetActionInfo(i)
    if (slotspellname and slotspellname == spellName) then
      return true
    end
  end

  return false
end

function LieLieBai:PetCanUseSpell(spellName)
  if not (UnitHealth("pet") > 0) then
    return false
  end

  for i = 1, NUM_PET_ACTION_SLOTS, 1 do
    local slotspellname, slotspellsubtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled =
      GetPetActionInfo(i)
    if (slotspellname and slotspellname == spellName) then
      local start, dur, enable = GetPetActionCooldown(i)
      if (dur > 0) then
        return false
      end
      if isActive then
        return false
      end
      return true
    end
  end

  return false
end

function LieLieBai:PetStay()
  unbuff("野兽之眼");
  PetWait();
end

function LieLieBai:XuKong()
  if not PetHasActionBar() then
    local spellId = self:getSpellId("召唤宠物")
    if spellId then
      CastSpell(spellId, BOOKTYPE_SPELL)
    end
  else
    PetWait()
    PetDefensiveMode()
  end
end

function LieLieBai:CanIUseMark()
  if GetNumRaidMembers() > 0 then
    return IsRaidLeader() or IsRaidOfficer()
  elseif GetNumPartyMembers() > 0 then
    return IsPartyLeader()
  else
    return false
  end
end

function LieLieBai:TargetDJSSJJ()
  TargetByName("达基萨斯将军", true)
  if self:CanIUseMark() then
    SetRaidTarget("target", 8)
  end
end

function LieLieBai:TargetYDDJELGZY()
  TargetByName("游荡的基尔罗格之眼", true)
  if self:CanIUseMark() then
    SetRaidTarget("target", 8)
  end
end

function LieLieBai:ZhaoMingJiuWei()
  local spellName = IsShiftKeyDown() and "宠物命令：就位" or "照明弹"
  local spellId = self:getSpellId(spellName)
  if spellId then
    CastSpell(spellId, BOOKTYPE_SPELL)
  end
end

function LieLieBai:RapidFire()
  if not UnitAffectingCombat("player") then
    return false
  end

  local spellId = self:getSpellId("急速射击")
  if spellId and self:IsSpellReady(spellId) then
    CastSpell(spellId, BOOKTYPE_SPELL)
    return true
  end

  return false
end

function LieLieBai:HunterMark()
  local spellId = self:getSpellId(L["猎人印记"])

  if not spellId then
    return false
  end

  if not UnitExists('target') then
    return false
  end

  if UnitIsDeadOrGhost('target') then
    return false
  end

  if not UnitCanAttack("player", "target") then
    return false
  end

  if self:isInGCD() then
    return false
  end

  if buffed(L["猎人印记"], "target") then
    return false
  end

  CastSpell(spellId, BOOKTYPE_SPELL)

  return true
end

function LieLieBai:PrintPetActionInfo()
  for i = 1, NUM_PET_ACTION_SLOTS, 1 do
    Printd("-----------------")
    local slotspellname, slotspellsubtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled =
      GetPetActionInfo(i)
    Printd("索引")
    Printd(i)
    Printd("技能名称")
    Printd(slotspellname)
    Printd("技能子文本")
    Printd(slotspellsubtext)
    Printd("技能图标")
    Printd(texture)
    Printd("是否是令牌")
    Printd(isToken)
    Printd("是否是激活状态")
    Printd(isActive)
    Printd("是否允许自动施法")
    Printd(autoCastAllowed)
    Printd("是否已启用自动施法")
    Printd(autoCastEnabled)
    Printd("-----------------")
  end
end

function LieLieBai:CastSpellIfNotInGCDAndReady(spellName)
  local spellId = self:getSpellId(spellName)

  if not spellId then
    return false
  end

  if not self:isInGCD() and self:IsSpellReady(spellId) then
    CastSpell(spellId, BOOKTYPE_SPELL)
    return true
  end

  return false
end

function LieLieBai:LLB()
  local db = LLB:GetDB()

  if db.hunterMark and self:HunterMark() then
    return
  end

  if self:AutoShot() then
    return
  end

  if db.rapidFire then
    self:RapidFire()
  end

  if db.useSmartSting and self:SmartSting() then
    return
  elseif db.sting ~= nil and db.sting ~= "none" and self:SmartSting(db.sting) then
    return
  end

  local spellPriority = {{
    name = "奥术射击",
    condition = function() return db.arcaneShot end,
  }, {
    name = "多重射击",
    condition = function() return db.multiShot end,
  }}

  for _, spell in ipairs(spellPriority) do
    if (not spell.condition or spell.condition()) and self:CastSpellIfNotInGCDAndReady(spell.name) then
      return
    end
  end
end
